package com.googlecode.sere.http

import com.googlecode.sere.util.Implicits._
import org.apache.commons.codec.binary.Base64
import com.googlecode.sere.translation.{MediaRange, MediaType, AcceptedTypes}

abstract class HttpRequest {

	val method: String
	
	val uri: String

  val body: String

  /**
   * Implementation of this abstract method allows the retrieval
   * of all the message headers
   */
  def headerElements(name: String): Seq[HeaderElement]

	def uniqueHeader(name: String): Option[HeaderElement] = {
		headerElements(name) match {
			case Seq() => None
			case Seq(value) => Some(value)
			case values => throw new Exception("Header %s has more than one value: %s" format (name, values))
		}
	}

  lazy val authorizationHeader =
    uniqueHeader("Authorization") map { element => HttpRequest.parseAuthorization(element.name) }

  lazy val acceptedTypes = {
    val pairs = headerElements("Accept") map { element =>
      val params = element.params - "q"
      val q = element.params.get("q") match {
        case Some(strQ) => BigDecimal(strQ)
        case None => BigDecimal(1)
      }
      val (mainType, subType) = MediaRange.parse(element.name)
      val mediaRange = MediaRange(mainType, subType, params)
      (mediaRange, q)
    }
    AcceptedTypes(Map(pairs:_*))
  }
  	
  lazy val contentType = {
    uniqueHeader("Content-Type") flatMap { ct =>
      val (mainType, subType) = MediaType.parse(ct.name)
      Some(MediaType(mainType, subType, ct.params))
    }
  }

}

object HttpRequest {

  def parseAuthorization(content: String) = {
    val Array(method, data, rest @ _*) = content.split("( )+")
    val (user, pass) = HttpRequest.decodeUserPass(data)
    (method, user, pass)    
  }

  def decodeUserPass(base64Str: String) = {
    val base64 = new Base64
    val str = new String(base64.decode(base64Str), "ISO-8859-1")
    val regex = "(.+?):(.+)".r
    str match {
      case regex(user, pass) => (user, pass)
      case _ => throw new BadRequest("Invalid credential format: %s" format str)
    }
  }
}